﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using GL;
using Crystal;

//Комментарии на русском: Содраны у Фомина как и код к которому такие комменты относятся ))
//Comments in English: Ivan

namespace _3d_motion_v0._0
{
    public partial class motion3d_form : Form
    {

        #region Ctr
        //---------------------------------------------------------------------------------------------//
        public motion3d_form()
        {
            InitializeComponent();
            // Операторы, делающие форму квадратной со стороной side, 
            // равной минимальной стороне дисплея. 
            // Класс Screen представляет дисплей или несколько дисплеев.
            // Статическое свойство PrimaryScreen класса Screen возвращает главный дисплей
            // Свойство Bounds возвращает прямоугольник границ дисплея
            int side =
                Math.Min(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height) / 2;
            // Свойство Size определяет размеры окна
            Size = new Size(side, side);

            //creating graphics context
            port = new glPort(viewer);

            //set defaults for gl port
            InitGL();

            viewer.MouseWheel += new MouseEventHandler(viewer_MouseWheel);

            //initializing Crystal routine
            crRoutine = new Routine();
        }

        /// <summary>
        /// Инициализирует некоторые атрибуты OpenGL.
        /// </summary>
        void InitGL()
        {
            // Цвет фона темно синий
            gl.ClearColor((float)clearColor.R / 255, (float)clearColor.G / 255,
                (float)clearColor.B / 255, (float)clearColor.A / 255);
            // Сведения о буферах
            statusLbl.Text = statusLbl.Text +
                " Color: Red: " + gl.Get(gl.RED_BITS, 1)[0] +
                " Green: " + gl.Get(gl.GREEN_BITS, 1)[0] +
                " Blue: " + gl.Get(gl.BLUE_BITS, 1)[0] +
                " Alpha: " + gl.Get(gl.ALPHA_BITS, 1)[0] +
                " Depth: " + gl.Get(gl.DEPTH_BITS, 1)[0] +
                " Stencil: " + gl.Get(gl.STENCIL_BITS, 1)[0] +
                " Acc: Red: " + gl.Get(gl.ACCUM_RED_BITS, 1)[0] +
                " Green: " + gl.Get(gl.ACCUM_GREEN_BITS, 1)[0] +
                " Blue: " + gl.Get(gl.ACCUM_BLUE_BITS, 1)[0] +
                "; Version: " + gl.GetString(gl.VERSION) +
                "; Vendor: " + gl.GetString(gl.VENDOR) +
                "; Renderer: " + gl.GetString(gl.RENDERER);


        }
        #endregion

        #region Constants and static fields
        //---------------------------------------------------------------------------------------------//
        /// <summary>
        /// Хранит цвет фона - цвет очистки буфера цвета (темно-синий)
        /// </summary>
        static Color clearColor = Color.FromArgb(240, 250, 245);
        /// <summary>
        /// An additional not used in user menu scene name providing the crystal structure display
        /// </summary>
        const string ADDITIONAL_SCENE_CRY = "crystal";
        #endregion

        #region fields and properties
        //---------------------------------------------------------------------------------------------//
        /// <summary>
        /// Positions of atoms in the current moment in normalized (to put to view) coordinates
        /// </summary>
        double[][] crAtoms;
        int traceLength = 3;
        double[][][] crAtoms_trace = new double[3][][];
        /// <summary>
        /// Crystal routine engine ))
        /// </summary>
        Routine crRoutine;
        Integration _intWindow;

        /// <summary>
        /// Хранит текущий порт воспроизведения команд OpenGL
        /// </summary>
        glPort port;
        /// <summary>
        /// Хранит текст выбранной команды подменю (имя сцены)
        /// </summary>
        string SceneName;
        /// <summary>
        /// Устанавливает и возвращает текущий индекс подменю (номер сцены)
        /// </summary>
        public static int SceneNmb
        {
            private set;
            get;
        }
        /// <summary>
        /// Хранит текст выбранной команды меню (имя акта)
        /// </summary>
        string ActName;
        /// <summary>
        /// Устанавливает и возвращает текущий индекс команды меню (номер акта)
        /// </summary>
        public static int ActNmb
        {
            private set;
            get;
        }
        /// <summary>
        /// Хранит текст текущего раздела главного меню (имя действия)
        /// </summary>
        string ActionName;
        /// <summary>
        /// Устанавливает и возвращает текущий индекс главного меню (номер действия)
        /// </summary>
        public static int ActionNmb
        {
            private set;
            get;
        }
        // поля, хранящие область отсечения
        float prjLeft, prjRight, prjBottom, prjTop, prjFar, prjNear;
        // поля, хранящие ширину и высоту порта наблюдения.
        float vpWidth, vpHeight;
        /// <summary>
        /// Хранит текущий угол поворота объектной системы координат вокруг своей оси.
        /// </summary>
        float angleRotate;
        /// <summary>
        /// Хранит текущий угол поворота объектной системы координат вокруг внешней оси.
        /// </summary>
        float angleTranslate;
        // поля, хранящие состояние активности вращения и трансляции 
        bool rotateEnabled, translateEnabled;
        /// <summary>
        /// Определяет масштаб вращения колесика мышки
        /// </summary>
        const float wheelScale = 0.003f;
        /// <summary>
        /// Хранит текущий масштабный коэффициент изображения объекта.
        /// </summary>
        float scaleCoeff;
        /// <summary>
        /// Хранит текущую ориентацию камеры вида по отношению к вертикальной оси.
        /// Угол cameraFi отсчитывается от вертикальной средней линии вправо 
        /// до Pi на правой границе панели и влево до -Pi на левой границе.
        /// </summary>
        double cameraFi;
        double cameraSinFi, cameraCosFi;
        /// <summary>
        /// Хранит текущую ориентацию камеры вида по отношению к горизонтальной оси.
        /// Угол cameraTeta отсчитывается от горизонтальной плоскости.
        /// Вверх - положительный, вниз - отрицательный. Это широта.
        /// cameraTeta лежит в интервале -Pi/2;Pi/2.
        /// </summary>
        double cameraTeta;
        double cameraSinTeta, cameraCosTeta;
        /// <summary>
        /// Хранит положение курсора в момент нажатия левой кнопки над glPanel
        /// </summary>
        Point curCursorPos;
        /// <summary>
        /// Радиус сферы наблюдения
        /// </summary>
        const float radiusEye = .001f;
        // Компоненты единичного вектора оси вращения.
        float rX, rY, rZ;
        /// <summary>
        /// Хранит текущий радиус окружности, в точки которой транслируется начало объектной стсьемы координат.
        /// </summary>
        float radiusTranslate;
        /// <summary>
        /// Хранит объект, возвращающий случайные числа.
        /// </summary>
        Random rnd = new Random();
        ///<summary>
        ///If axes are enabled
        ///</summary>
        bool enblAxes = false;

        private bool _integrationInProcess = false;

        private bool _timerEnabled { get { return (rotateEnabled || translateEnabled || _integrationInProcess); } }
        #endregion

        #region Controls and handlers
        //---------------------------------------------------------------------------------------------//
        private void motion3d_Load(object sender, EventArgs e)
        {
            //Nothing is here so far
            InitProjection();
        }

        private void parseInitCondsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            crRoutine.parseInit();
            if (crRoutine.mSys.Count == 0)
            {
                crRoutine.stWindow.addText("<motion3d_form> Mech System is not valid!", StatusWindow.TXT_WARNING);
                return;
            }
            crAtoms = new double[crRoutine.mSys.Count][];
            updateAtomsState();

            //todo: make it a single window!
            _intWindow = new Integration(StartStopIntegration);
            _intWindow.Show();

            SceneName = ADDITIONAL_SCENE_CRY;
            RenderScene();

        }

        private void updateAtomsState()
        {
            int i = 0;
            for (int j = 0; j < traceLength - 1; j++)
                crAtoms_trace[j] = crAtoms_trace[j + 1];
            crAtoms_trace[traceLength - 1] = new double[crAtoms.Length][];
            foreach (Atom atom in crRoutine.mSys)
            {
                crAtoms_trace[traceLength-1][i] = new double[atom.deg];
                crAtoms_trace[traceLength-1][i] = crAtoms[i];
                crAtoms[i] = new double[atom.deg];
                for (int j = 0; j < atom.deg; j++)
                    crAtoms[i][j] = atom.pos[j] / crRoutine.mSys.boxL;
                i++;
            }
        }

        private void potencialEnergyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            double[,] data = new double[2, 400];
            double x = 0.1;
            double[] pos0 = { 0, 0, 0 };
            double[] vel0 = { 0, 0, 0 };
            double[] pos;

            for (int i = 0; i < data.GetLength(1); i++)
            {
                x += 0.075;
                pos = new double[] { x, 0, 0 };
                data[0, i] = x;
                data[1, i] = Potencia(x);
            }

            WriteToFile("data.txt", data);
            // WriteToFile("data.txt", data);
        }

        private void motion3d_FormClosed(object sender, FormClosedEventArgs e)
        {
            port.Dispose();
        }

        //using for menu -> subMenu
        private void subMenuItem_Clicked(object sender, ToolStripItemClickedEventArgs e)
        {
            SceneName = (sender as ToolStripMenuItem).Text;
            SceneNmb = (sender as ToolStripMenuItem).Owner.Items.IndexOf(sender as ToolStripMenuItem);
            ActName = e.ClickedItem.Text;
            ActNmb = (sender as ToolStripMenuItem).DropDownItems.IndexOf(e.ClickedItem);
            ActionName = "undef";
            ActionNmb = -1;
            RenderScene();
        }

        //using for menu -> subMenu -> dropedItem
        private void subMenuItem_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            SceneName = e.ClickedItem.Text;
            SceneNmb = (sender as ToolStripMenuItem).DropDownItems.IndexOf(e.ClickedItem);
            ActName = (sender as ToolStripMenuItem).Text;
            ActNmb = (sender as ToolStripMenuItem).Owner.Items.IndexOf(sender as ToolStripMenuItem);
            ActionName = (sender as ToolStripMenuItem).OwnerItem.Text;
            ActionNmb = menuStrip.Items.IndexOf((sender as ToolStripMenuItem).OwnerItem);
            RenderScene();
        }

        private void viewer_SizeChanged(object sender, EventArgs e)
        {
            // Если контекст воспроизведения не установлен, то выход из метода
            if (null == port) return;
            gl.Viewport(0, 0, viewer.ClientSize.Width, viewer.ClientSize.Height);
            InitProjection();
            RenderFrame();
        }

        private void timer_Tick(object sender, EventArgs e)
        {
            if (translateEnabled) angleTranslate++;
            if (rotateEnabled) angleRotate++;
            if (_integrationInProcess) IntegrateMechSys();
            statusLbl.Text = "rotation angle = " + angleRotate.ToString();
            RenderFrame();
        }

        void viewer_MouseWheel(object sender, MouseEventArgs e)
        {
            scaleCoeff *= (float)(e.Delta > 0 ? 1.0 / (1.0 + wheelScale * e.Delta) : 1.0 + wheelScale * Math.Abs(e.Delta));
            RenderFrame();

        }

        private void viewer_MouseEnter(object sender, EventArgs e)
        {
            (sender as Control).Focus();
        }

        private void viewer_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
                curCursorPos = e.Location;
        }

        private void viewer_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && viewer.ClientRectangle.Contains(e.Location))
            {
                cameraFi += 2.0 * (e.X - curCursorPos.X) / viewer.ClientSize.Width * Math.PI;
                cameraTeta += (-1.0 * (e.Y - curCursorPos.Y) / viewer.ClientSize.Height) * Math.PI;
                // Запоминается новое положение курсора
                curCursorPos = e.Location;
                // Вычисляются тригонометрические функции, используемые
                // в LookAt для расчета матрицы преобразования
                cameraCosTeta = Math.Cos(cameraTeta);
                cameraSinTeta = Math.Sin(cameraTeta);
                cameraSinFi = Math.Sin(cameraFi);
                cameraCosFi = Math.Cos(cameraFi);
                RenderFrame();
            }

        }

        private void btnRotate_Click(object sender, EventArgs e)
        {
            btnRotate.Text = (rotateEnabled = !rotateEnabled) ? "Stop" : "Rotate";
            switchTimer();
        }

        private void switchTimer()
        {
            if (_timerEnabled)
                timer.Enabled = true;
            else
                timer.Enabled = false;
        }

        private void btnRestore_Click(object sender, EventArgs e)
        {
            InitModelViewTransfParameters();
            RenderFrame();
        }

        private void btnAxes_Click(object sender, EventArgs e)
        {
            if (enblAxes = !enblAxes)
            {
                btnAxes.Font = new Font(FontFamily.GenericSansSerif, 8.25f, FontStyle.Bold);
                RenderFrame();
            }
            else
            {
                btnAxes.Font = new Font(FontFamily.GenericSansSerif, 8.25f, FontStyle.Regular);
                RenderFrame();
            }
        }
        #endregion

        #region initialization methods
        //---------------------------------------------------------------------------------------------//
        void InitSceneAttributes()
        {
            InitModelViewTransfParameters();
            statusLbl.Text = string.Empty;
        }
        #endregion

        #region view methods
        //---------------------------------------------------------------------------------------------//
        /// <summary>
        /// Устанавливает воспроизводимую сцену и копирует буферы 
        /// </summary>
        void RenderFrame()
        {
            SetCurModelViewTransformation();
            BuildFrame();
            if (!port.SwapBuffers())
                throw new Exception("Не выполнен обмен буферов.");
        }

        private void viewer_Paint(object sender, PaintEventArgs e)
        {
            RenderFrame();
        }

        /// <summary>
        /// Устанавливает воспроизводимую сцену с помощью команд OpenGL
        /// </summary>
        void BuildFrame()
        {
            // Очистка буферов
            gl.Clear(gl.COLOR_BUFFER_BIT);
            gl.Clear(gl.DEPTH_BUFFER_BIT);
            gl.Enable(gl.DEPTH_TEST);

            // Построение изображения
            switch (SceneName)
            {
                case "View":
                    #region View
                    SetCurPointsActAttr();
                    switch (ActName)
                    {
                        case "Clear":    //0
                            break;

                        case "Point":    //1
                            double[] prMtrx = new double[16], mvMtrx = new double[16];
                            int[] vp = new int[4];
                            for (int i = 0; i < 16; i++)
                            {
                                prMtrx[i] = gl.Get(gl.PROJECTION_MATRIX, 16)[i];
                                mvMtrx[i] = gl.Get(gl.MODELVIEW_MATRIX, 16)[i];
                                if (i < 4) vp[i] = (int)gl.Get(gl.VIEWPORT, 4)[i];
                            }
                            float Xo, Yo, Zo;
                            Xo = 0; Yo = 0; Zo = 0;

                            gl.PointSize(1);
                            gl.Enable(gl.POINT_SMOOTH);
                            gl.Begin(gl.POINTS);
                            gl.Vertex(0, 0);
                            gl.End();

                            double Xw, Yw, Zw;
                            gl.Project(Xo, Yo, Zo, mvMtrx, prMtrx, vp, out Xw, out Yw, out Zw);
                            statusLbl.Text = string.Format(
                                "Xw={0:f}; Yw={1:f}; viewer: Width={2}; Height={3}; Центр: Ox={4}; Oy={5}",
                                Xw, Yw, viewer.ClientSize.Width, viewer.ClientSize.Height,
                                viewer.ClientSize.Width / 2, viewer.ClientSize.Height / 2);

                            break;

                        case "Circle":    //2
                            CreateNCallList(_isCircleListCreated, _circleList, CreateCircleList);
                            break;

                        case "Axes":      //3
                            drawAxes();
                            break;
                    }
                    #endregion
                    break;
                case ADDITIONAL_SCENE_CRY:
                    #region Crystal
                    displayAtoms();
                    #endregion
                    break;
            }
            if (enblAxes)
                drawAxes();

        }

        void RenderScene()
        {
            string actionInf = ActionName + " (" + ActionNmb.ToString() + ")";
            Text = SceneName + " (" + SceneNmb.ToString() + "): " +
                ActName + " (" + ActNmb.ToString() + "): " + (ActionNmb == -1 ? " " : actionInf);
            switch (SceneName)
            {
                case ADDITIONAL_SCENE_CRY:
                case "View":
                    InitSceneAttributes();
                    RenderFrame();
                    break;
                default:
                    break;
            }
        }

        void SetCurPointsActAttr()
        {
            gl.PointSize(1);
            gl.Enable(gl.POINT_SMOOTH);
            gl.Color(1, 0, 0);
        }

        /// <summary>
        /// Устанавливает преобразование от координат наблюдения к координатам отсечения
        /// </summary>
        void InitProjection()
        {
            // Определение текущей ширины и высоты порта наблюдения
            vpWidth = gl.Get(gl.VIEWPORT, 4)[2];
            vpHeight = gl.Get(gl.VIEWPORT, 4)[3];
            if (vpWidth > vpHeight)
            {
                prjLeft = -(prjRight = vpWidth / vpHeight); prjBottom = -(prjTop = 1);
            }
            else
            {
                prjLeft = -(prjRight = 1); prjBottom = -(prjTop = vpHeight / vpWidth);
            }
            prjFar = -(prjNear = (float)Math.Sqrt(3) + radiusEye);
            // Устанавливается режим ввода матрицы в стек преобразования 
            // от координат наблюдения к координатам отсечения 
            gl.MatrixMode(gl.PROJECTION);
            // Ввод единичной матрицы
            gl.LoadIdentity();
            // Умножает текущую матрицу на матрицу ортографической проекции 
            // с учетом пропорций порта наблюдения
            gl.Ortho(prjLeft, prjRight, prjBottom, prjTop, -prjNear, -prjFar);
            // Устанавливается режим ввода матрицы в стек преобразования 
            // от объектных координат  к координатам наблюдения 
            gl.MatrixMode(gl.MODELVIEW);
            gl.LoadIdentity();
        }

        /// <summary>
        /// Устанавливает текущую матрицу преобразования от объектных координат 
        /// к координатам наблюдения
        /// </summary>
        void SetCurModelViewTransformation()
        {
            gl.LoadIdentity();
            gl.Translate(radiusTranslate * (float)Math.Cos(angleTranslate * Math.PI / 180),
                radiusTranslate * (float)Math.Sin(angleTranslate * Math.PI / 180), 0);
            gl.Rotate(angleRotate, rX, rY, rZ);
            gl.Scale(scaleCoeff, scaleCoeff, scaleCoeff);
            // Установка "камеры наблюдения" - ввод специальной матрицы
            gl.LookAt(
                //Текущие координаты наблюдателя на сфере с центром в начале координат
                radiusEye * cameraCosTeta * cameraSinFi,
                radiusEye * cameraSinTeta,
                radiusEye * cameraCosTeta * cameraCosFi,
                //Неподвижная точка наблюдения
            0, 0, 0,
                //Направление вверх, нормальное к линии, соединяющей наблюдателя и
                // неподвижную точку (центр сферы)
            -cameraSinTeta * cameraSinFi, cameraCosTeta, -cameraSinTeta * cameraCosFi);
        }

        void InitModelViewTransfParameters()
        {
            rotateEnabled = translateEnabled = timer.Enabled = false;
            // Случайные углы направления оси вращения объектной системы координат.
            double fi = rnd.NextDouble() * 2 * Math.PI, teta = rnd.NextDouble() * Math.PI;
            // Единичный вектор оси вращения объектной системы координат.
            rX = (float)(Math.Sin(teta) * Math.Cos(fi));
            rY = (float)(Math.Sin(teta) * Math.Sin(fi));
            rZ = (float)Math.Cos(teta);
            scaleCoeff = 1;
            // Начальное положение камеры
            cameraTeta = .0; cameraFi = .0;
            cameraCosTeta = Math.Cos(cameraTeta);
            cameraSinTeta = Math.Sin(cameraTeta);
            cameraSinFi = Math.Sin(cameraFi);
            cameraCosFi = Math.Cos(cameraFi);
            angleRotate = angleTranslate = radiusTranslate = 0;
        }
        #endregion

        #region Calculation Logic
        //---------------------------------------------------------------------------------------------//
        double KineticEnergy(MechSys mechsys)
        {
            return 0.0;
        }

        double PotentialEnergy(MechSys mechsys)
        {

            return 0.0;
        }

        void WriteToFile(string filename, double[,] data)
        {
            StreamWriter sw = new StreamWriter(filename);
            int Nx = data.GetLength(0);
            int Ny = data.GetLength(1);
            for (int j = 0; j < Ny; j++)
            {
                for (int i = 0; i < Nx; i++)
                {
                    sw.Write(data[i, j].ToString() + "\t");
                }
                sw.Write("\n");
            }
            sw.Write("\n");
            sw.Close();
        }

        public double Densety()
        {
            return 0.0;
        }

        public double Potencia(double a)
        {
            //Не совсем ясно, почему каждый раз для каждой частицы надо пересоздавать систему?
            Atom[] atoms = new Atom[40];

            MechSys mech = new MechSys(atoms);

            mech.Add(new Atom(vect(0, 0, 0), vect(0, 0, 0), "I"));

            return mech.SelfPotentialEnergy();
        }

        public double[] vect(double x, double y, double z)
        {
            double[] vect = { x, y, z };
            return vect;
        }

        public void StartStopIntegration()
        {
            _integrationInProcess = !_integrationInProcess;
            switchTimer();
        }

        public void IntegrateMechSys()
        {
            crRoutine.mSys.DoOneTimeStep();
            updateAtomsState();
        }
        #endregion

        #region openGL drawing
        //---------------------------------------------------------------------------------------------//

        void displayAtoms()
        {
            drawPoints(crAtoms, 3, 0.5f, 0.1f, 0.1f);
        }

        void drawPoint(float x, float y, float z, float red, float green, float blue)
        {
            gl.PushAttrib(gl.POINT_BIT | gl.CURRENT_BIT);
            gl.PointSize(1);
            gl.Enable(gl.POINT_SMOOTH);

            gl.Begin(gl.POINTS);
            gl.Color(red, green, blue);
            gl.Vertex(x, y, z);
            gl.PopAttrib();
            gl.End();

        }

        void drawPoints(double[][] positions, int size, float red, float green, float blue)
        {
            gl.PushAttrib(gl.POINT_BIT | gl.CURRENT_BIT);
            gl.PointSize(size);
            gl.Enable(gl.POINT_SMOOTH);
            gl.Color(red, green, blue);

            gl.Begin(gl.POINTS);
            foreach (double[] pos in positions)
            {
                if (pos == null || pos.Length < 3)
                    continue;
                gl.Vertex((float)pos[0], (float)pos[1], (float)pos[2]);
            }
            gl.PopAttrib();
            gl.End();
        }

        void drawCircle_xy(float x, float y, float z, float R)
        {
            gl.Begin(gl.POINTS);
            for (int i = 0; i < 100; i++)
                gl.Vertex(x + R * (float)Math.Cos(2.0 * Math.PI * i / 100), y + R * (float)Math.Sin(2.0 * Math.PI * i / 100), z);
            gl.End();

        }

        void drawAxes()
        {
            gl.Begin(gl.LINES);
            gl.Color(1, 0, 0);
            gl.Vertex(-0.1f, 0); gl.Vertex(0.8f, 0);
            gl.Color(0, 1, 0);
            gl.Vertex(0, -0.1f); gl.Vertex(0, 0.8f);
            gl.Color(0, 0, 1);
            gl.Vertex(0, 0, -0.1f); gl.Vertex(0, 0, 0.8f);
            gl.End();
        }
        #endregion

        #region display lists stuff
        /// <summary>
        /// Определяет тип методов, создающих дисплейные списки объектов
        /// </summary>
        delegate void CreateList();
        /// <summary>
        /// Создает список дисплейных команд, одновременно вызывая его, либо вызывает уже готовый список
        /// </summary>
        /// <param name="isListCreated">
        /// Создан ли список
        /// </param>
        /// <param name="listNmb">
        /// Номер списка
        /// </param>
        /// <param name="createList">
        /// Метод создания списка класса CreateList
        /// </param>
        void CreateNCallList(bool isListCreated, uint listNmb, CreateList createList)
        {
            if (!isListCreated)
                createList();
            else
                gl.CallList(listNmb);
        }
        #region circle
        /// <summary>
        /// Хранит true, если список с нужным числом точек создан
        /// </summary>
        bool _isCircleListCreated;
        /// <summary>
        /// Хранит текущий номер дисплейного списка окружности из точек. 
        /// </summary>
        uint _circleList;
        /// <summary>
        /// Хранит текущее число точек в окружности.
        /// </summary>
        int _pointsNmb = 100;
        /// <summary>
        /// Устанавливает и возвращает число точек в списке окружности.
        /// При установке сбрасывает флаг создания создания списка.
        /// </summary>
        int pointsNmb
        {
            set { _pointsNmb = value; _isCircleListCreated = false; }
            get { return _pointsNmb; }
        }
        /// <summary>
        /// default circle radius
        /// </summary>
        float _circle_r = 0.8f;
        /// <summary>
        /// variable to change the circle radius interactively
        /// </summary>
        float circle_r
        {
            set { _circle_r = value; _isCircleListCreated = false; }
            get { return _circle_r; }
        }
        /// <summary>
        /// Создает дисплейный список окружности, состоящей из точек
        /// </summary>
        void CreateCircleList()
        {
            // Если список существует, то список стирается
            if (gl.IsList(_circleList))
                gl.DeleteLists(_circleList, 1);
            // Номер списка выбирается из свободных номеров
            _circleList = gl.GenLists(1);
            // Начало создания нового списка.
            // Список создается и тут же воспроизводится
            gl.NewList(_circleList, gl.COMPILE_AND_EXECUTE);
            // Команды списка - окружность из точек
            // В стек заталкиваются атрибуты точек и буфера цвета
            gl.PushAttrib(gl.POINT_BIT | gl.CURRENT_BIT);
            // Устанавливаются свои атрибуты точек и их цвет
            gl.PointSize(5);
            gl.Enable(gl.POINT_SMOOTH);
            gl.Color(0, 1, 1);
            // Изображаются точки на окружности, число которых определяется свойством pointsNmb
            gl.Begin(gl.POINTS);
            for (int i = 0; i < pointsNmb; i++)
                gl.Vertex(circle_r * (float)Math.Cos(2.0 * Math.PI * i / pointsNmb),
                    circle_r * (float)Math.Sin(2.0 * Math.PI * i / pointsNmb), 0);
            // Из стека удаляются временно установленные атрибуты точек и их цвет
            gl.PopAttrib();
            gl.End();
            // Завершаются команды списка
            gl.EndList();
            // Поднимается флаг создания списка – создан новый список окружности
            _isCircleListCreated = true;
        }

        #endregion
        #endregion


    }


}
